package test.socket;

import java.io.IOException;
import java.net.DatagramPacket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.fastjson.JSON;

import hyl.base.kv.cache.KvCacheBB;
import hyl.core.MyFun;
import hyl.core.fun.MyByte;
import hyl.core.io.MyFile;
import hyl.core.net.MyUdp;
import hyl.core.net.pg.Netpg2Msgs;
import hyl.core.net.pg.Netpg2Req1;
import hyl.core.run.MyRun;
import hyl.core.run.MyTimer;
import hyl.ext.web.ms.BInfo;

/**
 * 微服务注册,微服务删除,服务重启,服务停止,服务启动,服务升级,服务查看
 * MsA 会伴随 单点系统 启动
 * @author 37798955@qq.com
 *
 */
public class UMsA {
	// 60s过期=60000
	/**
	 * key: appid value:MsInfo
	 */
	ConcurrentHashMap<String, BInfo> msms = null;
	KvCacheBB _kvc = null;
	MyRun _mr = null;
	MyTimer _mt = null;

	public static void main(String[] args) {
		new UMsA();

	}

	public UMsA() {
		_kvc = new KvCacheBB("微服务库", "1");
		_mr = MyRun.getInstance("MyUdp接收", 4);
		_mt = MyTimer.getInstance("Myudp定时", 1);
		init();
	}
	BInfo im = new BInfo();
	MyUdp _srv;
	final int _过期毫秒=5000;
	final int _默认端口=6220;
	public void init() {
		im.init( MyFile.find("msa.ini"));

		// 载入库中所有客户端的配置
		msms = new ConcurrentHashMap<>();
		_kvc.do遍历(null, (k, v) -> {
			msms.put(MyFun.bytes2U8str(k), bytes2InfoMsm(v));
			return false;
		});

		int port = im.getAPort()==0?_默认端口: im.getAPort();
		启动服务(port);
		定时保存();
	}

	void 保存() {
		if (msms.isEmpty())
			return;
		long now = MyFun.getMs();
		msms.forEach((k, v) -> {
			if ((now - v.getPtime()) > _过期毫秒) {
				v.  stop() ;
			}
			_kvc.put(MyFun.u8str2Bytes(k), infoMsm2bytes(v));
		});
	}

	void 定时保存() {
		// 每隔10s保存一次
		_mt.start(() -> {
			保存();
		}, 10000);
	}

	/**
	 * 接入服务中心
	 */
	void 启动服务(int port) {
		_srv = new MyUdp(port);
		// 隐式实现ready
		_srv.receive((rp) -> {
			Netpg2Req1 netpg = new Netpg2Req1();
			// System.out.println("收到一条消息");
			byte[] bb = MyByte.subBytes(rp.getData(), 0, rp.getLength());

			// int len = rp.getLength();
			MyFun.print("MsmServer.start()", rp.getData().length, rp.getLength(), rp.getOffset());
			netpg.ini(bb);

			MyFun.print("_srv.receive", rp.getSocketAddress(), rp.getAddress(), rp.getPort());

			int cmd = netpg.getI指令();
			String str = netpg.getS内容();
			//远程ip
			String IP = rp.getSocketAddress().toString();
			if (cmd == 0) {
				onConn(str, IP, netpg, rp);
			} else if (cmd == 1) {// ping
				onPing(str);
			} else if (cmd == 2) { // err
				onErr(str, netpg);
			}else if (cmd ==4) {
				onTokenTest(str, rp);
			}
			return false;
		}, _mr);
	}

	/**
	 * 注册处理
	 */
	void onConn(String appid, String appIP, Netpg2Req1 netpg, DatagramPacket rp) {
		BInfo im = null;
		// 连接
		Map<String, String> info = BInfo.decodeToMap(netpg.getBb携参());
		MyFun.print("_srv.receive cmd=0", JSON.toJSONString(info));
		info.put("IP", appIP);
		appid = info.get("ID");
		// 如果id 不存在 注册一个
		if (MyFun.isEmpty(appid) || !msms.containsKey(appid)) {
			im = new BInfo();
			appid = MyFun.getUUID();
			info.put("ID", appid);
			im.setPub信息(info);

			msms.put(appid, im);

		} else { // 如果存在从数据库中取出
			im = msms.get(appid);
			im.ready();
			im.setPub信息(info);
		}
		try {
			Netpg2Msgs np = new Netpg2Msgs();
			np.set(1, appid, appIP);
			_srv.reply(np.toSendBytes(), rp);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 异常处理
	 */
	void onErr(String ID, Netpg2Req1 netpg) {
		String err = MyFun.bytes2U8str(netpg.getBb携参());
		MyFun.print("_srv.receive cmd=2", ID, err);
		BInfo im = msms.get(ID);
		im.err(err);
		msms.put(ID, im);
	}

	/**
	 * ping处理
	 */
	void onPing(String ID) {
		MyFun.print("_srv.receive cmd=1", ID);
		BInfo im = msms.get(ID);
		im.ping();
	}
	/**
	 * token判断处理
	 */
	void onTokenTest(String token, DatagramPacket rp) {
		MyFun.print("_srv.receive cmd=4", token);
		BInfo im = msms.get(token);
		im.ping();
		try {
			Netpg2Msgs np = new Netpg2Msgs();
			np.set(1, token);
			_srv.reply(np.toSendBytes(), rp);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static BInfo bytes2InfoMsm(byte[] 对象) {
		long ptm = MyFun.byteArray2Long(对象, 0);
		BInfo im = new BInfo();
		im.setPtime(ptm);
		im.decode(MyByte.subBytes(对象, 8));
		return im;
	}

	public static byte[] infoMsm2bytes(BInfo 对象) {
		byte[] pt = MyFun.long2ByteArray(对象.getPtime());
		return MyFun.concat(pt, 对象.encode());
	}
	public  Map<String,BInfo> getMap(){
		return msms;
	}

}
